package net.xo68.destiny.core.lock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 分布式锁管理者
 * @author wuxie
 * @version 2020-12-17
 */
public class DistributedLockManager {

    private static final Logger logger=LoggerFactory.getLogger(DistributedLockManager.class);

    /**
     * 尝试申请锁检查间隔
     */
    public static final long CHECK_INTERVAL_MILLISECONDS=500L;


    private final DistributedLockStore distributedLockStore;

    public DistributedLockManager(DistributedLockStore distributedLockStore) {
        this.distributedLockStore = distributedLockStore;
    }

    public DistributedLock tryLock(String lockKey, String lockHold, long tryTimeoutSecond){
        return tryLock(lockKey,lockHold,tryTimeoutSecond,0L);
    }
    /**
     * 尝试加锁，等待时间内无法取到锁，就放弃；另在周期内已经有运行成功的也放弃
     * @param lockKey
     * @param lockHold
     * @param tryTimeoutSecond
     * @param periodSecond
     * @return
     */
    public DistributedLock tryLock(String lockKey, String lockHold, long tryTimeoutSecond, long periodSecond){
        DistributedLock distributedLock=new DistributedLock();
        if(tryTimeoutSecond < 1){
            tryTimeoutSecond=1;
        }
        long nanos = TimeUnit.SECONDS.toNanos(tryTimeoutSecond);
        final ReentrantLock tryLock = distributedLock.getTryLock();
        final Condition timeoutCondition=distributedLock.getTimeoutCondition();
        try {
            tryLock.lockInterruptibly();
            DistributedLockInfo lockInfo=null;
            while (lockInfo==null){
                try{
                    lockInfo = distributedLockStore.tryLock(lockKey, lockHold, tryTimeoutSecond, periodSecond);
                    if(lockInfo!=null){
                        distributedLock.setLockInfo(lockInfo);
                    }else {
                        if( nanos <= 0){
                            break;
                        }
                        nanos = timeoutCondition.awaitNanos(TimeUnit.MILLISECONDS.toNanos(CHECK_INTERVAL_MILLISECONDS));
                    }
                }catch (Exception e){
                    logger.debug("操作锁存储状态异常,{} 锁已经存在了", lockKey);
                    if( nanos <= 0){
                        break;
                    }
                    nanos = timeoutCondition.awaitNanos(TimeUnit.SECONDS.toNanos(CHECK_INTERVAL_MILLISECONDS));
                }

            }
            if(lockInfo!=null){
                timeoutCondition.signal();
            }

        } catch (InterruptedException e) {
            logger.error(e.getMessage(),e);
        }finally {
            tryLock.unlock();
        }

        if(distributedLock.getLockInfo()==null){
            distributedLock=null;
        }

//        if(distributedLock!=null && logger.isDebugEnabled()){
//            logger.debug("申请锁成功，{}", lockKey);
//        }
//        if(distributedLock==null && logger.isDebugEnabled()){
//            logger.debug("申请锁失败，{}", lockKey);
//        }

        return distributedLock;
    }


    /**
     * 释放锁
     * @param distributedLock
     */
    public void unLock(DistributedLock distributedLock) {
        try{
            distributedLockStore.unLock(distributedLock.getLockInfo());
            //logger.debug("释放锁成功，{}", distributedLock);
        }catch (Exception e){
            logger.error("释放分布式锁异常", e);
        }
    }
}
